﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
//using System.Numerics;
using System.Xml.Serialization;

namespace ResolutionCalculator
{
    public record struct WidthAndHeight(int Width, int Height) { public static readonly WidthAndHeight Zero = new WidthAndHeight(0, 0); };
    [Serializable]
    public class AppConfigDataCollection
    {
        [XmlAttribute("noNamespaceSchemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
        public string noNamespaceSchemaLocation = "Configuration.xsd";

        /// <summary>
        /// 通过路径（包括网络地址）获取xml数据，并反序列化到对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Path"></param>
        /// <returns></returns>
        public static T PathToXmlDeserialize<T>(string Path)
        {
            using (System.Xml.XmlReader xr = System.Xml.XmlReader.Create(Path))
            {
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
                return (T)serializer.Deserialize(xr)!;
            }
        }
        /// <summary>
        /// 序列化到Xml文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Path"></param>
        /// <param name="t"></param>
        public static void XmlSerializerToXmlFile<T>(string Path, T t, bool EnableIndent = false)
        {
            using (System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(Path,
                new System.Xml.XmlWriterSettings { Encoding = System.Text.Encoding.UTF8, CloseOutput = true, Indent = EnableIndent }))
            {
                System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
                xs.Serialize(xw, t);
            }
        }
        public static string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.config");
        private static AppConfigDataCollection? m_GlobalConfig;
        public static AppConfigDataCollection GlobalConfig { get { if (m_GlobalConfig == null) { try { m_GlobalConfig = PathToXmlDeserialize<AppConfigDataCollection>(path); m_GlobalConfig.Sort(); } catch { m_GlobalConfig = new AppConfigDataCollection(); Save(); } } return m_GlobalConfig; } }
        public static void Save() { m_GlobalConfig?.Distinct(); m_GlobalConfig?.Sort(); XmlSerializerToXmlFile(path, m_GlobalConfig, true); }
        public static List<ResolutionData> Resolutions => GlobalConfig.ResolutionDatas;
        public static List<ProportionData> StandardProportions => GlobalConfig.ProportionDatas;

        public void Sort() { ResolutionDatas.Sort(); ProportionDatas.Sort(); }
        public void Distinct() 
        {
            ProportionDatas = ProportionDatas.Distinct(ProportionData.EqualityComparer).ToList();
            ResolutionDatas = ResolutionDatas.Distinct(ResolutionData.EqualityComparer).ToList();
        }
        public AppConfigDataCollection() { ResolutionDatas = new(); ProportionDatas = new(); }
        public List<ProportionData> ProportionDatas;
        public List<ResolutionData> ResolutionDatas;
        [XmlIgnore] public ResolutionData[] ProportionsinInResolutionDatas
        {
            get
            {
                var result = ResolutionDatas.Distinct(ResolutionData.ProportionEqualityComparer).OrderBy(p => p.ProportionArea).ToList();
                result.Insert(0, ResolutionData.Zero);
                return result.ToArray();
            }
        }
        [XmlIgnore] public WidthAndHeight[] Proportions => ResolutionDatas.Select(p => p.Proportion).ToArray();
    }

    [Serializable]
    public class ProportionData : IComparable<ProportionData>, IComparable, IEquatable<ProportionData>
    {
        public class ProportionDataEqualityComparer : IEqualityComparer<ProportionData> { public bool Equals(ProportionData? x, ProportionData? y) => x == null || y == null ? false : x.Equals(y); public int GetHashCode(ProportionData obj) => $"{obj.ProportionWidth}{obj.ProportionHeight}".GetHashCode(); }
        private readonly static IComparer<float> comparer = Comparer<float>.Default;
        public readonly static ProportionDataEqualityComparer EqualityComparer = new();
        public readonly static ProportionData Zero = new() { ProportionWidth = 0, ProportionHeight = 0, ProportionRatio = 0 };

        [XmlAttribute] public int ProportionWidth { get; init; }
        [XmlAttribute] public int ProportionHeight { get; init; }
        [XmlAttribute] public float ProportionRatio { get; init; }
        public ProportionData() { }
        public ProportionData(int ProportionWidth, int ProportionHeight) 
        {
            this.ProportionWidth = ProportionWidth;
            this.ProportionHeight = ProportionHeight;
            this.ProportionRatio = (float)Math.Round((float)ProportionWidth / ProportionHeight, 2, MidpointRounding.ToNegativeInfinity);
        }

        [XmlIgnore] public string ProportionLabe => $"{ProportionWidth.ToString().PadRight(2)} ：{ProportionHeight.ToString()}";
        [XmlIgnore] public float Area => (float)Math.Round((double)ProportionHeight * ProportionWidth, 2, MidpointRounding.AwayFromZero);
        public int CompareTo(ProportionData? other) => comparer.Compare(Area, other?.Area ?? float.NaN);
        public int CompareTo(object? obj) => CompareTo((ResolutionData?)obj);
        public bool Equals(ProportionData? other) => other?.ProportionWidth == ProportionWidth && other.ProportionHeight == ProportionHeight;
    }

    [Serializable]
    public class ResolutionData : IComparable<ResolutionData>, IComparable, IEquatable<ResolutionData>
    {
        public class ResolutionDataEqualityComparer : IEqualityComparer<ResolutionData> { public bool Equals(ResolutionData? x, ResolutionData? y) => x == null || y == null ? false : x.Equals(y); public int GetHashCode(ResolutionData obj) => $"{obj.ResolutionWidth}{obj.ResolutionHeight}".GetHashCode(); }
        public class ResolutionDataProportionEqualityComparer : IEqualityComparer<ResolutionData> { public bool Equals(ResolutionData? x, ResolutionData? y) => x == null || y == null ? false : x.ProportionEquals(y); public int GetHashCode(ResolutionData obj) => $"{obj.ProportionWidth}{obj.ProportionHeight}".GetHashCode(); }
        private readonly static IComparer<float> comparer = Comparer<float>.Default;
        public readonly static ResolutionDataEqualityComparer EqualityComparer = new();
        public readonly static ResolutionDataProportionEqualityComparer ProportionEqualityComparer = new();
        public readonly static ResolutionData Zero = new() { ResolutionWidth = 0, ResolutionHeight = 0, ProportionWidth = 0, ProportionHeight = 0 };

        private static bool TryGetStandardProportion(float value,out ProportionData? StandardProportion)
        {
            value = (float)Math.Round(value, 2, MidpointRounding.ToNegativeInfinity);
            StandardProportion = AppConfigDataCollection.StandardProportions.FirstOrDefault(p => Math.Abs(p.ProportionRatio - value) < 0.08f);
            if (StandardProportion == null)
                return false;
            return true;
        }
        private static int CalculateGCD(int a, int b)
        {
            //BigInteger.GreatestCommonDivisor
            int temp;
            while (b != 0)
            {
                temp = b;
                b = a % b;
                a = temp;
            }
            return a;
        }
        public static (int Width, int Height) GetProportion(int Width, int Height)
        {
            if (TryGetStandardProportion((float)Width / Height, out ProportionData? standardProportion))
                return (standardProportion?.ProportionWidth ?? 0, standardProportion?.ProportionHeight ?? 0);

            int GCD = CalculateGCD(Width, Height);
            if (GCD == 0)
                return (0, 0);
            return (Width / GCD, Height / GCD);
        }

        [XmlAttribute] public int ResolutionWidth { get; init; }
        [XmlAttribute] public int ResolutionHeight { get; init; }
        [XmlAttribute] public bool IsScreen { get; init; } = false;
        [XmlAttribute] public int ProportionWidth { get; init; }
        [XmlAttribute] public int ProportionHeight { get; init; }

        public ResolutionData() { }
        public ResolutionData(int ResolutionWidth, int ResolutionHeight, bool IsScreen = false)
        {
            this.ResolutionWidth = ResolutionWidth;
            this.ResolutionHeight = ResolutionHeight;
            (int Width, int Height) Proportion = GetProportion(ResolutionWidth, ResolutionHeight);
            this.ProportionWidth = Proportion.Width;
            this.ProportionHeight = Proportion.Height;
            this.IsScreen = IsScreen;
        }

        [XmlIgnore] public string ResolutionLabe => $"{ResolutionWidth}X{ResolutionHeight}";
        [XmlIgnore] public string ProportionLabe => this.ProportionEquals(Zero) ? "全部" : $"{ProportionWidth.ToString().PadRight(2)} ：{ProportionHeight}";
        [XmlIgnore] public WidthAndHeight Resolution => new WidthAndHeight(ResolutionWidth, ResolutionHeight);
        [XmlIgnore] public WidthAndHeight Proportion => new WidthAndHeight(ProportionWidth, ProportionHeight);
        [XmlIgnore] public float Area => (float)Math.Round((double)ResolutionHeight * ResolutionWidth, 2, MidpointRounding.AwayFromZero);
        [XmlIgnore] public float ProportionArea => (float)Math.Round((double)ProportionWidth * ProportionHeight, 2, MidpointRounding.AwayFromZero);
        public int CompareTo(ResolutionData? other) => comparer.Compare(Area, other?.Area ?? float.NaN);
        public int CompareTo(object? obj) => CompareTo((ResolutionData?)obj);
        public bool Equals(ResolutionData? other) => other?.ResolutionWidth == ResolutionWidth && other.ResolutionHeight == ResolutionHeight;
        public bool ProportionEquals(ResolutionData other) => other.ProportionWidth == ProportionWidth && other.ProportionHeight == ProportionHeight;
        public bool Equals(ResolutionData x, ResolutionData y) => x.Equals(y);

        public int GetHashCode([DisallowNull] ResolutionData obj) => obj.GetHashCode();

    }


    



}

